/**
 * TeachizenMailLibarary
 * ImapClient.java
 *
 * Copyright 2013 bnsworks
 * Written by htkim
 *	2013. 10. 9. first written.
 *
 * note:
 */
package com.teachizen.mail.receive.imap;

import java.util.*;

import javax.mail.*;

import lombok.extern.slf4j.*;

import com.sun.mail.imap.*;
import com.teachizen.mail.receive.*;

/**
 * @author cloudstick
 *
 */
@Slf4j
public class ImapClient {

	private ImapClient() { }

	private final static String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
	private final static ImapClient instance = new ImapClient();

	private static Properties props;
	private static Session session;

	private IMAPSSLStore store = null;
	private IMAPFolder folder = null;

	public static ImapClient getInstance() {
		return instance;
	}

	public void init(final ServerInfo serverInfo) {
		props = System.getProperties();
		
		/*
		props.setProperty("mail.imap.socketFactory.class", SSL_FACTORY);
	    props.setProperty("mail.imap.socketFactory.fallback", "false");
	    props.setProperty("mail.imap.port", serverInfo.port);
	    props.setProperty("mail.imap.socketFactory.port", serverInfo.port);
	    */
	    URLName url = new URLName("imap",
	    		serverInfo.url,
	    		Integer.parseInt(serverInfo.port),
	    		"",
	    		serverInfo.username,
	    		serverInfo.password);

		session = Session.getDefaultInstance(props, null);
		try {
			store = new IMAPSSLStore(session, url);
			store.connect();
		} catch(MessagingException e) {
			log.error("Exception occured while connecting imap.", e);
			throw new ReceiveException("Exception occured while connecting imap.", e);
		}
	}

	public void cleanup() {
		try {
			if (folder != null) {
				if (folder.isOpen()) {
					folder.close(true);
				}
			}
			if (store != null) {
				store.close();
			}
		} catch(Exception ignore) { }
	}

	public Message getMessage(final String folderName, final int number) {
		try {
			folder = (IMAPFolder) store.getFolder(folderName);
			folder.open(Folder.READ_WRITE);
			Message message = (Message)folder.getMessage(number);
			return message;
		} catch (MessagingException e) {
			log.error("Exception occured while getting imap message.", e);
			throw new ReceiveException("Exception occured while getting imap message.", e);
		}
	}

	public List<Message> getMessages(final String folderName) {
		try {
			folder = (IMAPFolder) store.getFolder(folderName);
			folder.open(Folder.READ_WRITE);
			Message[] messages = (Message[])folder.getMessages();
			if (messages == null || messages.length == 0) {
				log.info("Imap message is empty.");
				return null;
			}
			return new ArrayList<Message>(Arrays.asList(messages));
		} catch (MessagingException e) {
			log.error("Exception occured while getting imap messages.", e);
			throw new ReceiveException("Exception occured while getting imap messages.", e);
		}
	}

	public List<Message> getMessages(final String folderName, final int startNumber, final int endNumber) {
		try {
			folder = (IMAPFolder) store.getFolder(folderName);
			folder.open(Folder.READ_WRITE);
			Message[] messages = (Message[])folder.getMessages(startNumber, endNumber);
			if (messages == null || messages.length == 0) {
				log.info("Imap message is empty.");
				return null;
			}
			return new ArrayList<Message>(Arrays.asList(messages));
		} catch (MessagingException e) {
			log.error("Exception occured while getting imap messages.", e);
			throw new ReceiveException("Exception occured while getting imap messages.", e);
		}
	}

	public List<Message> getMessages(final String folderName, final int[] numbers) {
		try {
			folder = (IMAPFolder) store.getFolder(folderName);
			folder.open(Folder.READ_WRITE);
			Message[] messages = (Message[])folder.getMessages();
			if (messages == null || messages.length == 0) {
				log.info("Imap message is empty.");
				return null;
			}
			return new ArrayList<Message>(Arrays.asList(messages));
		} catch (MessagingException e) {
			log.error("Exception occured while getting imap messages.", e);
			throw new ReceiveException("Exception occured while getting imap messages.", e);
		}
	}

	public int getMessageCount(final String folderName, final boolean isNew) {
		try {
			folder = (IMAPFolder) store.getFolder(folderName);
			folder.open(Folder.READ_WRITE);
			return (isNew) ? folder.getNewMessageCount() : folder.getMessageCount();
		} catch (MessagingException e) {
			log.error("Exception occured while getting imap message count.", e);
			throw new ReceiveException("Exception occured while getting imap message count.", e);
		}
	}

	public int getUnreadMessageCount(final String folderName) {
		try {
			folder = (IMAPFolder) store.getFolder((folderName==null) ? "INBOX" : folderName);
			folder.open(Folder.READ_ONLY);
			return folder.getUnreadMessageCount();
		} catch (MessagingException e) {
			log.error("Exception occured while getting unread message count.", e);
			throw new ReceiveException("Exception occured while getting unread message count.", e);
		}
	}

	public List<Folder> getFolders() {
		try {
			Folder[] folders = store.getDefaultFolder().list("*");
			if (folders == null || folders.length == 0) {
				log.info("Imap folder list is empty.");
				return null;
			}
			return new ArrayList<Folder>(Arrays.asList(folders));
		} catch (MessagingException e) {
			log.error("Exception occured while getting imap folders.", e);
			throw new ReceiveException("Exception occured while getting imap folders.", e);
		}
	}

	public List<Folder> getFolders(String parentName) {
		try {
			Folder[] folders = store.getFolder(parentName).list("*");
			if (folders == null || folders.length == 0) {
				log.info("Parent[{}] folder list is empty.", parentName);
				return null;
			}
			return new ArrayList<Folder>(Arrays.asList(folders));
		} catch (MessagingException e) {
			log.error("Exception occured while getting imap folders.", e);
			throw new ReceiveException("Exception occured while getting imap folders.", e);
		}
	}
}
